package com.mlethe.library.calendarview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

/**
 * 日历布局
 * 各个类使用包权限，避免不必要的public
 *
 * @Author Mlethe
 * @Date 2022/12/29
 */
public class CalendarView extends LinearLayout {

    private final CalendarViewDelegate mDelegate;

    /**
     * 星期栏
     */
    private WeekBar mWeekBar;

    /**
     * 自定义自适应高度的ViewPager
     */
    private MonthViewPager mMonthPager;

    public CalendarView(@NonNull Context context) {
        this(context, null);
    }

    public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mDelegate = new CalendarViewDelegate(context, attrs);
        setOrientation(LinearLayout.VERTICAL);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        Context context = getContext();
        try {
            Constructor<?> constructor = mDelegate.getWeekBarClass().getConstructor(Context.class);
            mWeekBar = (WeekBar) constructor.newInstance(getContext());
        } catch (Exception e) {
            e.printStackTrace();
            mWeekBar = new WeekBar(context);
        }
        mWeekBar.setup(mDelegate);
        mWeekBar.onWeekStartChange(mDelegate.getWeekStart());
        addView(mWeekBar, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        mMonthPager = new MonthViewPager(context);
        mMonthPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
        addView(mMonthPager, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        mMonthPager.setup(mDelegate);
        mMonthPager.scrollToCurrent(false);
    }

    /**
     * 获取 CalendarViewDelegate
     *
     * @return
     */
    CalendarViewDelegate getDelegate() {
        return mDelegate;
    }

    /**
     * 获取星期栏view
     *
     * @return 星期栏view
     */
    protected WeekBar getWeekBar() {
        return mWeekBar;
    }

    /**
     * 获取月视图view
     *
     * @return 月视图view
     */
    protected MonthViewPager getMonthViewPager() {
        return mMonthPager;
    }

    /**
     * 设置月起始日
     *
     * @param monthStartDay 月起始日
     * @return 月份是否改变，true 改变，否则false
     */
    public boolean setMonthStartDay(int monthStartDay) {
        return mMonthPager.setMonthStartDay(monthStartDay);
    }

    /**
     * 设置显示模式为全部
     */
    public void setAllMode() {
        setShowMode(CalendarViewDelegate.MODE_ALL_MONTH);
    }

    /**
     * 设置显示模式为仅当前月份
     */
    public void setOnlyCurrentMode() {
        setShowMode(CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH);
    }

    /**
     * 设置显示模式为填充
     */
    public void setFixMode() {
        setShowMode(CalendarViewDelegate.MODE_FIT_MONTH);
    }

    /**
     * 设置显示模式
     * {@link CalendarViewDelegate#MODE_ALL_MONTH}
     * {@link CalendarViewDelegate#MODE_ONLY_CURRENT_MONTH}
     * {@link CalendarViewDelegate#MODE_FIT_MONTH}
     *
     * @param mode 月视图显示模式
     */
    private void setShowMode(int mode) {
        if (mode != CalendarViewDelegate.MODE_ALL_MONTH &&
                mode != CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH &&
                mode != CalendarViewDelegate.MODE_FIT_MONTH)
            return;
        if (mDelegate.getMonthViewShowMode() == mode)
            return;
        mDelegate.setMonthViewShowMode(mode);
        mMonthPager.updateData();
    }

    /**
     * 设置星期日周起始
     */
    public void setWeekStarWithSun() {
        setWeekStart(CalendarViewDelegate.WEEK_START_WITH_SUN);
    }

    /**
     * 设置星期一周起始
     */
    public void setWeekStarWithMon() {
        setWeekStart(CalendarViewDelegate.WEEK_START_WITH_MON);
    }

    /**
     * 设置星期六周起始
     */
    public void setWeekStarWithSat() {
        setWeekStart(CalendarViewDelegate.WEEK_START_WITH_SAT);
    }

    /**
     * 设置周起始
     * {@link CalendarViewDelegate#WEEK_START_WITH_SUN}
     * {@link CalendarViewDelegate#WEEK_START_WITH_MON}
     * {@link CalendarViewDelegate#WEEK_START_WITH_SAT}
     *
     * @param weekStart 周起始
     */
    private void setWeekStart(int weekStart) {
        if (weekStart != CalendarViewDelegate.WEEK_START_WITH_SUN &&
                weekStart != CalendarViewDelegate.WEEK_START_WITH_MON &&
                weekStart != CalendarViewDelegate.WEEK_START_WITH_SAT)
            return;
        if (weekStart == mDelegate.getWeekStart())
            return;
        mDelegate.setWeekStart(weekStart);
        mWeekBar.onWeekStartChange(weekStart);
        mWeekBar.onDateSelected(mDelegate.getSelectedCalendar(), weekStart, false);
        mMonthPager.updateData();
    }

    /**
     * 设置默认选中日期为当月第一天
     */
    public final void setDefaultMonthViewSelectDay() {
        setDefaultCalendarSelectMode(CalendarViewDelegate.FIRST_DAY_OF_MONTH);
    }

    /**
     * 设置默认选中日期跟随上个月，当月为今天
     */
    public final void setLastMonthViewSelectDay() {
        setDefaultCalendarSelectMode(CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY);
    }

    /**
     * 设置默认选中日期跟随上个月，忽视今天
     */
    public final void setLastMonthViewSelectDayIgnoreCurrent() {
        setDefaultCalendarSelectMode(CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT);
    }

    /**
     * 设置默认选中不选中，滚动到指定日期才会选中
     */
    public final void setChangeMonthSelectNone() {
        setDefaultCalendarSelectMode(CalendarViewDelegate.CHANGE_MONTH_SELECT_NONE);
    }

    /**
     * 设置默认天的选择的模式
     * {@link CalendarViewDelegate#FIRST_DAY_OF_MONTH}
     * {@link CalendarViewDelegate#LAST_MONTH_VIEW_SELECT_DAY}
     * {@link CalendarViewDelegate#LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT}
     * {@link CalendarViewDelegate#CHANGE_MONTH_SELECT_NONE}
     *
     * @param mode 默认天的选择的模式
     */
    private void setDefaultCalendarSelectMode(int mode) {
        if (mode != CalendarViewDelegate.FIRST_DAY_OF_MONTH &&
                mode != CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY &&
                mode != CalendarViewDelegate.LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT &&
                mode != CalendarViewDelegate.CHANGE_MONTH_SELECT_NONE)
            return;
        if (mode == mDelegate.getDefaultCalendarSelectMode()) return;
        mDelegate.setDefaultCalendarSelectMode(mode);
    }

    /**
     * 设置月视图item高度
     *
     * @param calendarItemHeight MonthView item height
     */
    public final void setCalendarItemHeight(int calendarItemHeight) {
        if (mDelegate.getCalendarItemHeight() == calendarItemHeight) {
            return;
        }
        mDelegate.setCalendarItemHeight(calendarItemHeight);
        mMonthPager.updateItemHeight();
    }

    public void setCalendarPadding(int calendarPadding) {
        setCalendarPadding(calendarPadding, calendarPadding);
    }

    public void setCalendarPadding(int calendarPaddingLeft, int calendarPaddingRight) {
        if (mDelegate.getCalendarPaddingLeft() == calendarPaddingLeft && mDelegate.getCalendarPaddingRight() == calendarPaddingRight) {
            return;
        }
        mDelegate.setCalendarPadding(calendarPaddingLeft, calendarPaddingRight);
        updateCalendarPadding();
    }

    public void setCalendarPaddingLeft(int calendarPaddingLeft) {
        if (mDelegate.getCalendarPaddingLeft() == calendarPaddingLeft) return;
        mDelegate.setCalendarPaddingLeft(calendarPaddingLeft);
        updateCalendarPadding();
    }

    public void setCalendarPaddingRight(int calendarPaddingRight) {
        if (mDelegate.getCalendarPaddingRight() == calendarPaddingRight) return;
        mDelegate.setCalendarPaddingRight(calendarPaddingRight);
        updateCalendarPadding();
    }

    /**
     * 更新CalendarPadding
     */
    private void updateCalendarPadding() {
        mWeekBar.updateCalendarPadding();
        mMonthPager.updateCalendarPadding();
    }

    /**
     * 设置日期范围
     *
     * @param minYear      最小年份
     * @param minYearMonth 最小年份对应月份
     * @param maxYear      最大月份
     * @param maxYearMonth 最大月份对应月份
     */
    public void setRange(int minYear, int minYearMonth, int maxYear, int maxYearMonth) {
        if (!CalendarUtil.isRange(minYear, minYearMonth, maxYear, maxYearMonth)) return;
        mDelegate.setRange(minYear, minYearMonth, maxYear, maxYearMonth);
        int monthStartDay = mDelegate.getMonthStartDay();
        Calendar selectedCalendar = mDelegate.getSelectedCalendar();
        if (!CalendarUtil.isCalendarInRange(selectedCalendar, minYear, minYearMonth, maxYear, maxYearMonth, monthStartDay)) {
            Calendar selectCalendar = CalendarUtil.getSelectCalendar(minYear, minYearMonth, monthStartDay, mDelegate.getCurrentDay(), mDelegate.getDefaultCalendarSelectMode(), selectedCalendar);
            if (selectCalendar != null) {
                mDelegate.setSelectedCalendar(selectCalendar);
            }
        }
        mMonthPager.updateRange();
    }

    /**
     * 设置当前时间
     *
     * @param time     时间戳
     * @param timeZone 时区
     */
    public final void setCurrentTime(long time, TimeZone timeZone) {
        if (mDelegate == null) return;
        mDelegate.setCurrentTime(time, timeZone);
        updateCurrentDate();
    }

    /**
     * 更新当天日期
     */
    public final void updateCurrentDate() {
        if (mDelegate == null || mMonthPager == null) return;
        mDelegate.updateCurrentDay();
        mMonthPager.updateCurrentDate();
    }

    /**
     * 标记哪些日期有事件
     *
     * @param mSchemeDates mSchemeDatesMap 通过自己的需求转换即可
     */
    public final void setSchemeDate(Map<String, Calendar> mSchemeDates) {
        mDelegate.mSchemeDatesMap = mSchemeDates;
        mMonthPager.updateScheme();
    }

    /**
     * 清空日期标记
     */
    public final void clearSchemeDate() {
        mDelegate.mSchemeDatesMap = null;
        mDelegate.clearSelectedScheme();
        mMonthPager.updateScheme();
    }

    /**
     * 添加事物标记
     *
     * @param calendar calendar
     */
    public final void addSchemeDate(Calendar calendar) {
        if (calendar == null || !calendar.isAvailable()) {
            return;
        }
        if (mDelegate.mSchemeDatesMap == null) {
            mDelegate.mSchemeDatesMap = new HashMap<>();
        }
        mDelegate.mSchemeDatesMap.remove(calendar.toString());
        mDelegate.mSchemeDatesMap.put(calendar.toString(), calendar);
        mMonthPager.updateScheme();
    }

    /**
     * 添加事物标记
     *
     * @param schemeDates schemeDates
     */
    public final void addSchemeDate(Map<String, Calendar> schemeDates) {
        if (mDelegate == null || schemeDates == null || schemeDates.isEmpty()) {
            return;
        }
        if (mDelegate.mSchemeDatesMap == null) {
            mDelegate.mSchemeDatesMap = new HashMap<>();
        }
        mDelegate.addSchemes(schemeDates);
        mMonthPager.updateScheme();
    }

    /**
     * 移除某天的标记
     * 这个API是安全的
     *
     * @param calendar calendar
     */
    public final void removeSchemeDate(Calendar calendar) {
        if (calendar == null) {
            return;
        }
        if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.isEmpty()) {
            return;
        }
        mDelegate.mSchemeDatesMap.remove(calendar.toString());
        mMonthPager.updateScheme();
    }

    /**
     * 获取选择的日期
     *
     * @return 获取选择的日期
     */
    public Calendar getSelectedCalendar() {
        return mDelegate.getSelectedCalendar();
    }

    /**
     * 滚动到指定年月
     *
     * @param year  年
     * @param month 月
     */
    public void scrollToCalendar(int year, int month) {
        scrollToCalendar(year, month, true);
    }

    /**
     * 滚动到指定年月
     *
     * @param year         年
     * @param month        月
     * @param smoothScroll 是否有动画
     */
    public void scrollToCalendar(int year, int month, boolean smoothScroll) {
        mMonthPager.scrollToCalendar(year, month, smoothScroll);
    }

    /**
     * 滚动到指定日期
     *
     * @param year  年
     * @param month 月
     * @param day   日
     */
    public void scrollToCalendar(int year, int month, int day) {
        scrollToCalendar(year, month, day, true);
    }

    /**
     * 滚动到指定日期
     *
     * @param year         年
     * @param month        月
     * @param day          日
     * @param smoothScroll 是否有动画
     */
    public void scrollToCalendar(int year, int month, int day, boolean smoothScroll) {
        mMonthPager.scrollToCalendar(year, month, day, smoothScroll);
    }

    /**
     * 滚动到选择的日历
     */
    public void scrollToSelectCalendar() {
        Calendar selectedCalendar = mDelegate.getSelectedCalendar();
        scrollToCalendar(selectedCalendar.getYear(),
                selectedCalendar.getMonth(),
                selectedCalendar.getDay(),
                true);
    }


    /**
     * 滚动到当前日期
     */
    public void scrollToCurrent() {
        scrollToCurrent(true);
    }

    /**
     * 滚动到当前日期
     *
     * @param smoothScroll 是否有动画
     */
    public void scrollToCurrent(boolean smoothScroll) {
        mMonthPager.scrollToCurrent(smoothScroll);
    }

    /**
     * 滚动到上一个月
     */
    public void scrollToPre() {
        scrollToPre(true);
    }

    /**
     * 滚动到上一个月
     *
     * @param smoothScroll 是否有动画
     */
    public void scrollToPre(boolean smoothScroll) {
        scrollToPre(1, smoothScroll);
    }

    /**
     * 向当前月份之前滚动几个月
     *
     * @param num          滚动的数量
     * @param smoothScroll 是否有动画
     */
    public void scrollToPre(int num, boolean smoothScroll) {
        int currentItem = mMonthPager.getCurrentItem();
        int position = currentItem - num;
        if (position < 0) {
            if (currentItem > 0) {
                mMonthPager.setCurrentItem(0, smoothScroll);
            } else {
                CalendarView.OnScrollOutRangeListener scrollOutRangeListener = mDelegate.mScrollOutRangeListener;
                if (scrollOutRangeListener != null) {
                    scrollOutRangeListener.onScrolledToFirstPage();
                }
            }
            return;
        }
        mMonthPager.setCurrentItem(position, smoothScroll);
    }

    /**
     * 滚动到下一个月
     */
    public void scrollToNext() {
        scrollToNext(true);
    }

    /**
     * 滚动到下一个月
     *
     * @param smoothScroll 是否有动画
     */
    public void scrollToNext(boolean smoothScroll) {
        scrollToNext(1, smoothScroll);
    }

    /**
     * 向当前月份之后滚动几个月
     *
     * @param num          滚动的数量
     * @param smoothScroll 是否有动画
     */
    public void scrollToNext(int num, boolean smoothScroll) {
        int currentItem = mMonthPager.getCurrentItem();
        int position = currentItem + num;
        int monthCount = mMonthPager.getMonthCount();
        int maxPosition = monthCount - 1;
        if (position > maxPosition) {
            if (currentItem < maxPosition) {
                mMonthPager.setCurrentItem(maxPosition, smoothScroll);
            } else {
                CalendarView.OnScrollOutRangeListener scrollOutRangeListener = mDelegate.mScrollOutRangeListener;
                if (scrollOutRangeListener != null) {
                    scrollOutRangeListener.onScrolledToLastPage();
                }
            }
            return;
        }
        mMonthPager.setCurrentItem(position, smoothScroll);
    }

    /**
     * 设置背景色
     *
     * @param weekBackground 星期栏背景色
     */
    public void setBackground(int weekBackground) {
        mWeekBar.setBackgroundColor(weekBackground);
    }


    /**
     * 设置文本颜色
     *
     * @param currentDayTextColor 今天字体颜色
     * @param curMonthTextColor   当前月份字体颜色
     * @param otherMonthColor     其它月份字体颜色
     */
    public void setTextColor(int currentDayTextColor, int curMonthTextColor, int otherMonthColor) {
        if (mDelegate == null || mMonthPager == null) {
            return;
        }
        mDelegate.setTextColor(currentDayTextColor, curMonthTextColor, otherMonthColor);
        mMonthPager.updateStyle();
    }

    /**
     * 设置选择的效果
     *
     * @param selectedThemeColor 选中的标记颜色
     * @param selectedTextColor  选中的字体颜色
     */
    public void setSelectedColor(int selectedThemeColor, int selectedTextColor) {
        if (mDelegate == null || mMonthPager == null) {
            return;
        }
        mDelegate.setSelectColor(selectedThemeColor, selectedTextColor);
        mMonthPager.updateStyle();
    }

    /**
     * 定制颜色
     *
     * @param selectedThemeColor 选中的标记颜色
     * @param schemeColor        标记背景色
     */
    public void setThemeColor(int selectedThemeColor, int schemeColor) {
        if (mDelegate == null || mMonthPager == null) {
            return;
        }
        mDelegate.setThemeColor(selectedThemeColor, schemeColor);
        mMonthPager.updateStyle();
    }

    /**
     * 设置标记的色
     *
     * @param schemeColor     标记背景色
     * @param schemeTextColor 标记字体颜色
     */
    public void setSchemeColor(int schemeColor, int schemeTextColor) {
        if (mDelegate == null || mMonthPager == null) {
            return;
        }
        mDelegate.setSchemeColor(schemeColor, schemeTextColor);
        mMonthPager.updateStyle();
    }

    /**
     * 月份改变事件
     *
     * @param listener listener
     */
    public void setOnMonthChangeListener(OnMonthChangeListener listener) {
        this.mDelegate.mMonthChangeListener = listener;
    }

    /**
     * 日期选择事件
     *
     * @param listener listener
     */
    public void setOnCalendarSelectListener(OnCalendarSelectListener listener) {
        this.mDelegate.mCalendarSelectListener = listener;
    }

    /**
     * 日期长按事件
     *
     * @param listener listener
     */
    public void setOnCalendarLongClickListener(OnCalendarLongClickListener listener) {
        this.mDelegate.mCalendarLongClickListener = listener;
    }

    /**
     * 周选择事件
     *
     * @param listener listener
     */
    public void setOnWeekSelectListener(OnWeekSelectListener listener) {
        this.mDelegate.mWeekSelectListener = listener;
    }

    /**
     * 周长按事件
     *
     * @param listener listener
     */
    public void setOnWeekLongClickListener(OnWeekLongClickListener listener) {
        this.mDelegate.mWeekLongClickListener = listener;
    }

    /**
     * 滑动超限事件
     *
     * @param listener listener
     */
    public void setOnScrollOutRangeListener(OnScrollOutRangeListener listener) {
        this.mDelegate.mScrollOutRangeListener = listener;
    }

    /**
     * 月份切换事件
     */
    public interface OnMonthChangeListener {
        void onMonthChange(int year, int month);
    }

    /**
     * 日历选择事件
     */
    public interface OnCalendarSelectListener {

        /**
         * 超出范围越界
         *
         * @param calendar calendar
         */
        void onCalendarOutOfRange(@NonNull Calendar calendar);

        /**
         * 日期选择事件
         *
         * @param calendar calendar
         * @param isClick  是否为点击选中
         */
        void onCalendarSelect(@NonNull Calendar calendar, boolean isClick);
    }

    /**
     * 日历长按事件
     */
    public interface OnCalendarLongClickListener {

        /**
         * 超出范围越界
         *
         * @param calendar calendar
         */
        void onCalendarLongClickOutOfRange(@NonNull Calendar calendar);

        /**
         * 日期长按事件
         *
         * @param calendar calendar
         */
        void onLongClick(@NonNull Calendar calendar);
    }

    /**
     * 周选择事件
     */
    public interface OnWeekSelectListener {
        /**
         * 周日期选中
         *
         * @param weekCalendars 选中日期列表
         * @param isClick       是否为点击选中
         */
        void onWeekSelect(@NonNull List<Calendar> weekCalendars, boolean isClick);
    }

    /**
     * 周长按事件
     */
    public interface OnWeekLongClickListener {
        /**
         * 周日期选中
         *
         * @param weekCalendars 选中日期列表
         */
        void onLongClick(@NonNull List<Calendar> weekCalendars);
    }

    /**
     * 滑动超限事件
     */
    public interface OnScrollOutRangeListener {
        /**
         * 已滑动到第一页
         */
        void onScrolledToFirstPage();

        /**
         * 已滑动到最后一页
         */
        void onScrolledToLastPage();
    }
}
